import { DEBUG, JSB } from "cc/env";
import { singleton } from "../decorator";
import { nameOfClazz } from "../utils";


@singleton
export default class HttpManager implements IManager {
    public static readonly instance: HttpManager;

    launched: boolean;

    /** 请求标记，递增 */
    private _seq: number = 1;


    /** http 请求地址 */
    private _host: string = 'https://www.baidu.com';

    public constructor() {
        this.launched = true;
    }

    protected onLaunched(): void {
        if (DEBUG) console.debug(`[${nameOfClazz(this)}] launched ...`);
    }

    /** 清空数据 */
    public clear(): void {
        this._seq = 1;
    }

    public setHost(host: string): void {
        this._host = host;
    }

    /**
     * Get
     * @param url 
     * @param params 
     * @param handler 
     * @param async 
     * @param customData 
     * @param extraHeaders 
     */
    public httpGet(url: string, params: object, handler:(response: HttpResponse)=>void, async: boolean = false, customData: object = null, extraHeaders: object = {}): void {
        this.httpRequest(url, 'GET', params, handler, async, customData, extraHeaders);
    }

    /**
     * Post
     * @param url 
     * @param params 
     * @param handler 
     * @param async 
     * @param customData 
     * @param extraHeaders 
     */
    public httpPost(url: string, params: object, handler:(response: HttpResponse)=>void, async: boolean = false, customData: object = null, extraHeaders: object = {}): void {
        this.httpRequest(url, 'POST', params, handler, async, customData, extraHeaders);
    }

    /**
     * HttpRequest
     * @param url           地址
     * @param mothed        方法 POST/GET
     * @param params        参数
     * @param handler       回调
     * @param async         是否异步请求
     * @param customData    自定义数据 
     * @param extraHeaders  临时headers
     */
    private httpRequest(url: string, mothed: 'GET' | 'POST', params: object, handler: (response: HttpResponse) => void, async: boolean = false, customData: object = null, extraHeaders: object = {}) {
        let xhr = new XMLHttpRequest();

        if (!/^http:\/\//.test(url) && !/^https:\/\//.test(url)) {
            url = this._host + url;
        }

        if (mothed == "GET") {
            if (params) {
                url = url + this._encode(params);
            }
        }
        this.registerScriptHandler(this._seq++, xhr, handler, url, mothed, customData);
        xhr.open(mothed, url, async);
        async && (xhr.timeout = 5000);
        this._extraHeaders(xhr, extraHeaders);
        mothed == "GET" && xhr.send();
        mothed == "POST" && xhr.send(JSON.stringify(params));
    }

    /**
     * 将参数转为字符串
     * @param params
     */
    private _encode(params: object): string {
        let str = '?';
        if(params != null && params != undefined) {
            for (const key in params) {
                if (Object.prototype.hasOwnProperty.call(params, key)) {
                    str += str + key + "=" + params[key] + '&';
                }
            }
        }
        return str;
    }

    /**
    * 注册网络请求事件回调
    * @param seq       请求sequence标记，递增
    * @param xhr       xhr实例
    * @param handler   请求回调
    * @param url       网络请求地址
    * @param method    请求类型 GET POST
    * @param params    请求参数
    */
    private registerScriptHandler(seq: number, xhr: XMLHttpRequest, handler: (response: HttpResponse) => void, url: string, method: 'GET' | 'POST', customData: any): void {
        DEBUG && console.log(`[C->S] [${method}.${seq}] -> ${url}`);

        // 失败和超时回调
        ['loadstart', 'abort', 'error', 'load', 'loadend', 'timeout'].forEach(function (eventname) {
            xhr["on" + eventname] = function () {
                if (eventname == "error" || eventname == "timeout") {
                    let response: HttpResponse = { seq: seq, code: xhr.status, mothed: method, status: eventname, result: xhr.responseText, customData: customData };
                    (!JSB) && DEBUG && console.log(`%c[S->C] [${method}.${seq}] -> ${url} ${eventname} ${xhr.status}`, `color:#f00;`);
                    (JSB) && DEBUG && console.log(`[S->C] [${method}.${seq}] -> ${url} ${eventname} ${xhr.status}`);
                    handler && handler(response);
                }
            };
        });

        // 请求完成回调
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304)) { // 成功
                    let response: HttpResponse = { seq: seq, code: xhr.status, mothed: method, status: "success", result: xhr.responseText, customData: customData };
                    (!JSB) && DEBUG && console.log(`[S->C] [${method}.${seq}] -> ${url}%c success ${xhr.status}\n%c${response.result}`, `color:#19A316;`, `color:#19A316;padding: 3px;border-radius:2px;border:1px solid #000a;`);
                    (JSB) && DEBUG && console.log(`[S->C] [${method}.${seq}] -> ${url} success ${xhr.status}\n${response.result}`);
                    handler && handler(response);
                } else { // 失败
                    let response: HttpResponse = { seq: seq, code: xhr.status, mothed: method, status: "failed", errMsg: xhr.responseText, customData: customData };
                    (!JSB) && DEBUG && console.log(`%c[S->C] [${method}.${seq}] -> ${url} failed ${xhr.status} ${response.errMsg}`, `color:#f00;`);
                    (JSB) && DEBUG && console.log(`[S->C] [${method}.${seq}] -> ${url} failed ${xhr.status} ${response.errMsg}`);
                    handler && handler(response);
                }

            }
        };
    }

    private _extraHeaders(xhr: XMLHttpRequest, extraHeaders: object = {}): void {
        // Default Headers
        //xhr.setRequestHeader("Content-Type", "application/json");
        //xhr.setRequestHeader('Access-Control-Allow-Origin', '*');
        // xhr.setRequestHeader('Authorization', 'Bearer ' + "");
        // Extra Headers
        for (const key in extraHeaders) {
            if (Object.prototype.hasOwnProperty.call(extraHeaders, key)) {
                const value = extraHeaders[key];
                xhr.setRequestHeader(key, value);
            }
        }
    }

}

export interface HttpResponse {
    seq: number,
    code: number,
    mothed: string,
    status: string,
    errMsg?: string,
    result?: string,
    customData?: object
}
